package io.jpress.pro.attachment.aliyun;

import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.aliyun.oss.model.GetObjectRequest;
import com.jfinal.kit.LogKit;
import com.jfinal.kit.PathKit;
import io.jboot.app.config.JbootConfigManager;
import io.jboot.utils.FileUtil;
import io.jboot.utils.StrUtil;
import io.jboot.web.attachment.AttachmentContainer;

import java.io.File;
import java.io.InputStream;

/**
 * @author michael yang (fuhai999@gmail.com)
 */
public class AliyunOssAttachmenetContainer implements AttachmentContainer {

    private String basePath = PathKit.getWebRootPath();
    private AliyunOssAttachmentConfig config = JbootConfigManager.me().get(AliyunOssAttachmentConfig.class);


    public AliyunOssAttachmenetContainer() {

    }

    public AliyunOssAttachmenetContainer(String basePath, AliyunOssAttachmentConfig config) {
        this.basePath = basePath;
        this.config = config;
    }

    @Override
    public String saveFile(File file) {
        if (!config.isEnable()) {
            return null;
        }
        String relativePath = getRelativePath(file);
        upload(relativePath, file);
        return relativePath;
    }


    @Override
    public String saveFile(File file, String toRelativePath) {
        if (!config.isEnable()) {
            return null;
        }
        upload(toRelativePath, file);
        return toRelativePath;
    }

    @Override
    public String saveFile(InputStream inputStream, String toRelativePath) {
        if (!config.isEnable()) {
            return null;
        }
        upload(toRelativePath, inputStream);
        return toRelativePath;
    }


    @Override
    public boolean deleteFile(String relativePath) {
        if (!config.isEnable()) {
            return false;
        }
        relativePath = removeFirstFileSeparator(relativePath);
        OSSClient ossClient = createOSSClient();
        try {
            ossClient.deleteObject(config.getBucketname(), relativePath);
        } catch (Exception ex) {
            LogKit.error(ex.toString(), ex);
        } finally {
            ossClient.shutdown();
        }
        return true;
    }


    @Override
    public File getFile(String relativePath) {
        if (!config.isEnable()) {
            return null;
        }
        File localFile = new File(basePath, relativePath);
        if (localFile.exists()) {
            return localFile;
        }
        if (download(relativePath, localFile)) {
            return localFile;
        }
        return null;
    }


    @Override
    public String getRelativePath(File file) {
        if (!config.isEnable()) {
            return null;
        }
        return FileUtil.removePrefix(file.getAbsolutePath(), basePath);
    }


    /**
     * 同步本地文件到阿里云OSS
     *
     * @param path
     * @param file
     * @return
     */
    public boolean upload(String path, File file) {
        if (StrUtil.isBlank(path)) {
            return false;
        }

        path = removeFirstFileSeparator(path);
        path = path.replace('\\', '/');

        String ossBucketName = config.getBucketname();
        OSSClient ossClient = createOSSClient();

        try {
            ossClient.putObject(ossBucketName, path, file);
            boolean success = ossClient.doesObjectExist(ossBucketName, path);
            if (!success) {
                LogKit.error("aliyun oss upload error! path:" + path + "\nfile:" + file);
            }
            return success;

        } catch (Throwable e) {
            LogKit.error("aliyun oss upload error!!!", e);
            return false;
        } finally {
            ossClient.shutdown();
        }
    }


    public boolean upload(String path, InputStream inStream) {
        if (StrUtil.isBlank(path)) {
            return false;
        }

        path = removeFirstFileSeparator(path);
        path = path.replace('\\', '/');

        String ossBucketName = config.getBucketname();
        OSSClient ossClient = createOSSClient();

        try {
            ossClient.putObject(ossBucketName, path, inStream);
            boolean success = ossClient.doesObjectExist(ossBucketName, path);
            if (!success) {
                LogKit.error("aliyun oss upload error! path:" + path);
            }
            return success;

        } catch (Throwable e) {
            LogKit.error("aliyun oss upload error!!!", e);
            return false;
        } finally {
            ossClient.shutdown();
        }
    }


    /**
     * 如果文件以 / 或者 \ 开头，去除 / 或 \ 符号
     */
    private static String removeFirstFileSeparator(String path) {
        while (path.startsWith("/") || path.startsWith("\\")) {
            path = path.substring(1);
        }
        return path;
    }

    /**
     * 下载 阿里云 OSS 到本地
     *
     * @param path
     * @param toFile
     * @return
     */
    public boolean download(String path, File toFile) {
        if (StrUtil.isBlank(path)) {
            return false;
        }
        path = removeFirstFileSeparator(path);
        OSSClient ossClient = createOSSClient();
        try {
            if (!toFile.getParentFile().exists() && !toFile.getParentFile().mkdirs()) {
                LogKit.error("dir can not create! {}", toFile.getParentFile());
            }

            if (!toFile.exists() && !toFile.createNewFile()) {
                LogKit.error("file can not create! {}", toFile);
            }
            ossClient.getObject(new GetObjectRequest(config.getBucketname(), path), toFile);
            return true;
        } catch (Throwable e) {
            LogKit.error("aliyun oss download error!!!  path:" + path + "   toFile:" + toFile, e);
            if (toFile.exists() && !toFile.delete()) {
                LogKit.error("file can not delete! {}", toFile);
            }
            return false;
        } finally {
            ossClient.shutdown();
        }
    }


    private OSSClient createOSSClient() {
        String endpoint = config.getEndpoint();
        String accessId = config.getAccessKeyId();
        String accessKey = config.getAccessKeySecret();
        return new OSSClient(endpoint, new DefaultCredentialProvider(accessId, accessKey), null);
    }


}
